/*
  This file is part of 'Trivial chess'.

  'Trivial chess' is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  'Trivial chess' is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with 'Trivial chess'.  If not, see <http://www.gnu.org/licenses/>.
*/

/* SVN FILE: $Id$ */
/**
 * @author         $Author$
 * @version        $Rev$
 * @lastrevision   $Date$
 * @filesource     $URL$
 */

#ifndef MOVEABSTRACT_H_
#define MOVEABSTRACT_H_

#include "../MeAbstract.h"
#include "../IoAbstract.h"

template<template<class> class Move, class B>
class MoveAbstract
	: public MeAbstract<Move<B> >
	, public IoAbstract<Move<B> > {
public:
	typedef B Board;
	typedef typename Board::Square Square;
	typedef typename Square::File File;
	typedef typename Square::Rank Rank;
	typedef typename Board::Man Man;
	typedef typename Man::Piece Piece;
	typedef typename Man::Color Color;
public:
	static const Move<B> NIL() {
		return Move<B>(Square::NIL(), Square::NIL());
	}
public:
	Man man(const Board& board) const {
		return board[this->me_c().from()];
	}

	Man manWas(const Board& board) const {
		return this->me_c().promoted().isNil()
			? board[this->me_c().to()]
			: Man(board[this->me_c().to()].color(),Piece::PAWN());
	}

	Piece piece(const Board& board) const {
		return this->me_c().man(board).piece();
	}

	Piece pieceWas(const Board& board) const {
		return this->me_c().manWas(board).piece();
	}

	Color color(const Board& board) const {
		return this->me_c().man(board).color();
	}

	Color colorWas(const Board& board) const {
		return this->me_c().manWas(board).color();
	}

	Man capturedMan(const Board& board) const {
		return board[this->me_c().to()];
	}

	template<class UnmakeInfo>
	Man capturedManWas(const UnmakeInfo& info) const {
		return info.capturedMan();
	}

	Piece capturedPiece(const Board& board) const {
		return this->me_c().capturedMan(board).piece();
	}

	template<class UnmakeInfo>
	Piece capturedPieceWas(const UnmakeInfo& info) const {
		return this->me_c().capturedManWas(info).piece();
	}

	bool isCastling(const Board& board) const {
		return this->me_c().piece(board).isKing()
			&& this->me_c().from().file() == File::E()
			&& (this->me_c().to().file() == File::G()
				|| this->me_c().to().file() == File::C());
	}

	bool wasCastling(const Board& board) const {
		return this->me_c().pieceWas(board).isKing()
			&& this->me_c().from().file() == File::E()
			&& (this->me_c().to().file() == File::G()
				|| this->me_c().to().file() == File::C());
	}

	bool isKingCastling(const Board& board) const {
		return this->me_c().piece(board).isKing()
			&& this->me_c().from().file() == File::E()
			&& this->me_c().to().file() == File::G();
	}

	bool wasKingCastling(const Board& board) const {
		return this->me_c().pieceWas(board).isKing()
			&& this->me_c().from().file() == File::E()
			&& this->me_c().to().file() == File::G();
	}

	bool isQueenCastling(const Board& board) const {
		return this->me_c().piece(board).isKing()
			&& this->me_c().from().file() == File::E()
			&& this->me_c().to().file() == File::C();
	}

	bool wasQueenCastling(const Board& board) const {
		return this->me_c().pieceWas(board).isKing()
			&& this->me_c().from().file() == File::E()
			&& this->me_c().to().file() == File::C();
	}

	bool isPromotion() const {
		return !this->me_c().promoted().isNil();
	}

	bool wasPromotion() const {
		return this->me_c().isPromotion();
	}

	bool isCapture(const Board& board) const {
		return !this->me_c().capturedMan(board).isNil();
	}

	template<class UnmakeInfo>
	bool wasCapture(const UnmakeInfo& info) const {
		return !this->me_c().capturedManWas(info).isNil();
	}

	bool isEnpassant(const Board& board) const {
		const Square enpassant = board.enpassant();

		return !enpassant.isNil()
			&& this->me_c().piece(board).isPawn()
			&& this->me_c().to() == enpassant;
	}

	template<class UnmakeInfo>
	bool wasEnpassant(const Board& board, const UnmakeInfo& info) const {
		const Square enpassant = info.enpassant();

		return !enpassant.isNil()
			&& this->me_c().pieceWas(board).isPawn()
			&& this->me_c().to() == enpassant;
	}

	bool isPawnDoubleAdvance(const Board& board) const {
		return (this->me_c().man(board).isWhitePawn()
				&& this->me_c().from().rank() == Rank::R2()
				&& this->me_c().to().rank() == Rank::R4())
			|| (this->me_c().man(board).isBlackPawn()
				&& this->me_c().from().rank() == Rank::R7()
				&& this->me_c().to().rank() == Rank::R5());
	}

	bool wasPawnDoubleAdvance(const Board& board) const {
		return (this->me_c().manWas(board).isWhitePawn()
				&& this->me_c().from().rank() == Rank::R2()
				&& this->me_c().to().rank() == Rank::R4())
			|| (this->me_c().manWas(board).isBlackPawn()
				&& this->me_c().from().rank() == Rank::R7()
				&& this->me_c().to().rank() == Rank::R5());
	}

	bool operator==(const Move<B> move) const {
		return this->me_c().to() == move.to()
			&& this->me_c().from() == move.from();
	}

	std::string toStr() const {
		if ( this->me_c().from() == Square::NIL() )
			return "-";
		else {
			std::string ret = this->me_c().from().toStr() + this->me_c().to().toStr();
			const Piece promoted = this->me_c().promoted();
			if ( !promoted.isNil() )
				ret += promoted.toStr();
			return ret;
		}
	}

	void fromStr(const std::string str) {

		if ( "-" == str ) {
			this->me() = Move<B>::NIL();
			return;
		}

		const Square from = Square(str.substr(0,2));
		this->me().setFrom(from);
		const Square to = Square(str.substr(2,2));
		this->me().setTo(to);

		if ( str.length() > 4 ) {
			const Piece promoted = Piece(str.substr(4,1));
			this->me().setPromoted(promoted);
		}
		else
			this->me().setPromoted(Piece::NIL());
	}
};

#endif /* MOVEABSTRACT_H_ */
